#include <cstdint>
#include <iostream>
#include <vector>
#include <bitset>
#include <memory>
#include <string>
#include <fstream>

#define OFFSET 256

#define CATEGORY_MASK 0XC0000000
#define CATEGORY_CONTROL 0X40000000
#define CATEGORY_ARITH 0XC0000000
#define GET_CATEGORY(i) (CATEGORY_MASK & (i)) 

#define OPCODE_MASK 0X3C000000
#define GET_OPCODE(i) (((i) & OPCODE_MASK) >> 26)

#define RS_MASK 0X03E00000
#define RT_MASK 0X001F0000
#define RD_MASK 0X0000F800
#define IM_MASK 0X0000FFFF

#define GET_RS(i) ((RS_MASK & (i)) >> 21)
#define GET_RT(i) ((RT_MASK & (i)) >> 16)
#define GET_RD(i) ((RD_MASK & (i)) >> 11)
#define GET_IM(i) (IM_MASK & (i))

#define TARGET_MASK 0X03FFFFFF
#define GET_TARGET(i) ((i) & TARGET_MASK)

#define OFFSET_MASK 0X0000FFFF
#define GET_OFFSET(i) ((i) & OFFSET_MASK)

#define SA_MASK 0X000007C0
#define GET_SA(i) (((i) & SA_MASK) >> 6)

#define OP_J      0X00000000
#define OP_JR     0X00000001
#define OP_BEQ    0X00000002
#define OP_BLTZ   0X00000003
#define OP_BGTZ   0X00000004
#define OP_BREAK  0X00000005
#define OP_SW     0X00000006
#define OP_LW     0X00000007
#define OP_SLL    0X00000008
#define OP_SRL    0X00000009
#define OP_SRA    0X0000000A
#define OP_NOP    0X0000000B

#define OP_ADD    0X00000000
#define OP_SUB    0X00000001
#define OP_MUL    0X00000002
#define OP_AND    0X00000003
#define OP_OR     0X00000004
#define OP_XOR    0X00000005
#define OP_NOR    0X00000006
#define OP_SLT    0X00000007
#define OP_ADDI   0X00000008
#define OP_ANDI   0X00000009
#define OP_ORI    0X0000000A
#define OP_XORI   0X0000000B

using u32 = uint32_t;
using i32 = int32_t;
using namespace std;

enum Category { CONTROL, ARITH, SIGNAL };
Category get_category(u32 instruction);

/// map for register
vector<i32> R(32, 0);

/// lowest add
constexpr u32 lowest_addr = 340;
constexpr u32 largest_addr = 404;

/// map for memory data
vector<i32> D{-1, -2, -3, 1, 2, 4, -4, 10, 7, 9,1, 0, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0};

/// PC for program
u32 PC = 0;

/// name for op
vector<string> OPNAME[2] = {
  {"J", "JR", "BEQ", "BLTZ", "BGTZ", "BREAK", "SW", "LW", "SLL", "SRL", "SRA", "NOP"},
  {"ADD", "SUB", "MUL", "AND", "OR", "XOR", "NOR", "SLT", "ADDI", "ANDI", "ORI", "XORI"}
};

/// Operand for register, immediate
struct Operand {
  /// get value of operand 
  virtual i32 get_val() = 0;

  /// set value for operand
  virtual void set_val(i32) = 0;

  /// make operand to string
  virtual string to_string() = 0;
  virtual ~Operand() = default;
};

/// Instruction for arithmetic and control
struct Instruction {
  u32 raw; // original instruction
  u32 opcode; // opcode
  Category category; // category of the instruction
  string assemble; // assemble instruction
  u32 addr; // address of current instruction

  Instruction(u32 raw): raw(raw), opcode(GET_OPCODE(raw)) {
    category = get_category(raw);
  }

  virtual void translate() = 0; // translate the raw -> assemble
  virtual void run() = 0; // run the instruction
  virtual ~Instruction() = default;
};

struct Register: Operand {
  u32 id; // id of register

  Register(u32 id): id(id) {}

  i32 get_val() override {
    return R[id];
  }

  void set_val(i32 v) override {
    R[id] = v;
  }

  string to_string() override {
    return string("R") + std::to_string(id);
  }
};

struct Immediate: Operand {
  i32 val; // value of operand

  Immediate(i32 val): val(val) {}

  i32 get_val() override {
    return val;
  }

  void set_val(i32 v) override {
    val = v;
  }

  string to_string() override {
    return string("#") + std::to_string(val);
  }

};

struct Offset: Immediate {
  Offset(i32 val): Immediate(val) {}

  string to_string() override {
    return std::to_string(val);
  }
};


struct ControlInstruction: Instruction {
  unique_ptr<Operand> rs;
  unique_ptr<Operand> rt;
  unique_ptr<Operand> rd;
  unique_ptr<Operand> sa;
  unique_ptr<Operand> offset;
  unique_ptr<Operand> target;

  ControlInstruction(u32 raw): Instruction(raw) {}

  void translate() override {
    
    // fill the operands
    if (opcode == OP_J) {
      target = make_unique<Immediate>(GET_TARGET(raw) << 2);
      assemble = OPNAME[category][opcode] + " " + target->to_string();
    } else if (opcode == OP_JR) {
      rs = make_unique<Register>(GET_RS(raw));
      assemble = OPNAME[category][opcode] + " " + rs->to_string();
    } else if (opcode == OP_BEQ) {
      rs = make_unique<Register>(GET_RS(raw));
      rt = make_unique<Register>(GET_RT(raw));
      offset = make_unique<Immediate>(static_cast<i32>(GET_OFFSET(raw) << 2)); //! convert to signed-18
      assemble = OPNAME[category][opcode] + " "
                + rs->to_string() + ", "
                + rt->to_string() + ", "
                + offset->to_string();
    } else if (opcode == OP_BLTZ || opcode == OP_BGTZ) {
      rs = make_unique<Register>(GET_RS(raw));
      offset = make_unique<Immediate>(static_cast<i32>(GET_OFFSET(raw) << 2)); //! convert to signed-18
      assemble = OPNAME[category][opcode] + " " + rs->to_string() + ", " + offset->to_string();
    } else if (opcode == OP_SW || opcode == OP_LW) {
      rs = make_unique<Register>(GET_RS(raw));
      rt = make_unique<Register>(GET_RT(raw));
      offset = make_unique<Offset>(static_cast<int16_t>(GET_OFFSET(raw)));
      // lowest_addr = min(static_cast<u32>(offset->get_val()), lowest_addr); // hard into
      // largest_addr = max(static_cast<u32>(offset->get_val()), largest_addr); // hard into
      assemble = OPNAME[category][opcode] + " " + rt->to_string() + ", "
                + offset->to_string() + "(" + rs->to_string() + ")";
    } else if (opcode == OP_SLL || opcode == OP_SRL || opcode == OP_SRA) {
      rt = make_unique<Register>(GET_RT(raw));
      rd = make_unique<Register>(GET_RD(raw));
      sa = make_unique<Immediate>(GET_SA(raw));
      assemble = OPNAME[category][opcode] + " "
                  + rd->to_string() + ", " + rt->to_string()
                  + ", " + sa->to_string();
    } else if (opcode == OP_NOP || opcode == OP_BREAK) {
      assemble = OPNAME[category][opcode];
    }

  }

  void run() override {
    if (opcode == OP_J) PC = (target->get_val() - OFFSET) / 4;
    else if (opcode == OP_JR) PC = (rs->get_val() - OFFSET) / 4; 
    else if (opcode == OP_BEQ) if (rs->get_val() == rt->get_val()) PC++, PC += offset->get_val() / 4; else PC++;
    else if (opcode == OP_BLTZ) if (rs->get_val() < 0) PC++, PC += offset->get_val() / 4; else PC++;
    else if (opcode == OP_BGTZ) if (rs->get_val() > 0) PC++, PC += offset->get_val() / 4; else PC++;
    else if (opcode == OP_SW) D[(rs->get_val() + offset->get_val() - lowest_addr) / 4] = rt->get_val(), PC++;
    else if (opcode == OP_LW) rt->set_val(D[(rs->get_val() + offset->get_val() - lowest_addr) / 4]), PC++;
    else if (opcode == OP_SLL) rd->set_val(rt->get_val() << sa->get_val()), PC++;
    else if (opcode == OP_SRL) rd->set_val(static_cast<u32>(rt->get_val()) >> sa->get_val()), PC++;
    else if (opcode == OP_SRA) rd->set_val(rt->get_val() >> sa->get_val()), PC++;
    else if (opcode == OP_BREAK) PC = 0XFFFFFFFF;
    
  }
};

struct ArithInstruction: Instruction {
  unique_ptr<Operand> rs;
  unique_ptr<Operand> rt;
  unique_ptr<Operand> ri; // rd or im


  ArithInstruction(u32 raw): Instruction(raw) {}

  void translate() override {

    // generate assemble
    if (opcode == OP_ADD || opcode == OP_SUB 
          || opcode == OP_MUL || opcode == OP_AND
          || opcode == OP_OR || opcode == OP_XOR
          || opcode == OP_NOR || opcode == OP_SLT) {
      rs = make_unique<Register>(GET_RS(raw));
      rt = make_unique<Register>(GET_RT(raw));
      ri = make_unique<Register>(GET_RD(raw));
      assemble = OPNAME[category][opcode] + " " 
                + ri->to_string() + ", "
                + rs->to_string() + ", "
                + rt->to_string();
    } else if (opcode == OP_ADDI || opcode == OP_ANDI
                || opcode == OP_ORI || opcode == OP_XORI) {
      rs = make_unique<Register>(GET_RS(raw));
      rt = make_unique<Register>(GET_RT(raw));
      ri = make_unique<Immediate>(GET_IM(raw));
      assemble = OPNAME[category][opcode] + " "
                + rt->to_string() + ", "
                + rs->to_string() + ", "
                + ri->to_string();
    }
  }

  void run() override {
    // based on opcode run the instruction
    if (opcode == OP_ADD)         ri->set_val(rs->get_val() + rt->get_val());
    else if (opcode == OP_SUB)    ri->set_val(rs->get_val() - rt->get_val());
    else if (opcode == OP_MUL)    ri->set_val(rs->get_val() * rt->get_val());
    else if (opcode == OP_AND)    ri->set_val(rs->get_val() & rt->get_val());
    else if (opcode == OP_OR)     ri->set_val(rs->get_val() | rt->get_val());
    else if (opcode == OP_XOR)    ri->set_val(rs->get_val() ^ rt->get_val());
    else if (opcode == OP_NOR)    ri->set_val(~(rs->get_val() | rt->get_val()));
    else if (opcode == OP_SLT)    ri->set_val(rs->get_val() < rt->get_val());
    else if (opcode == OP_ADDI)   rt->set_val(rs->get_val() + ri->get_val());
    else if (opcode == OP_ANDI)   rt->set_val(rs->get_val() & ri->get_val());
    else if (opcode == OP_ORI)    rt->set_val(rs->get_val() | ri->get_val());
    else if (opcode == OP_XORI)   rt->set_val(rs->get_val() ^ ri->get_val());
    PC += 1;
  }
};

struct SignalInstruction: Instruction {
  SignalInstruction(u32 raw): Instruction(raw) { }

  void translate() override {
    assemble = std::to_string(static_cast<i32>(raw));
  }

  void run() override {
    // do nothing
  }
  
};


struct Program {
  vector<unique_ptr<Instruction>> instructions;
  u32 cycle = 0;
  fstream fout, fsim;

  Program() { 
    fout.open("myanswer.txt", ios::out);
    if (!fout) cerr << "Can't open file answer.txt" << endl;

    fsim.open("mysimulation.txt", ios::out);
    if (!fsim) cerr << "Can't open file simulation.txt" << endl;
  }

  ~Program() { fout.close(); fsim.close(); }
  
  /// binary instruction decode as assemble
  void disassembly();

  /// run the instruction
  void run();

  /// read the instruction
  void read();

  /// trace the register and memory data
  void trace(u32 addr, const string& assemble);
};




int main() {
  freopen("sample.txt", "r", stdin);
  Program p;
  p.read();
  p.disassembly();
  p.run();
  return 0;
}

Category get_category(u32 instruction) {
  if (GET_CATEGORY(instruction) == CATEGORY_ARITH) return Category::ARITH;
  else if (GET_CATEGORY(instruction) == CATEGORY_CONTROL) return Category::CONTROL;
  return Category::SIGNAL;
}

void Program::disassembly() {
  for (u32 i = 0; i != instructions.size(); i++) {
    auto& instruction = instructions[i];
    instruction->addr = i * 4 + OFFSET;
    fout << bitset<32>(instruction->raw) << "\t" << i * 4 + OFFSET << '\t' << instruction->assemble << endl;
  }
}

void Program::trace(u32 addr, const string& assemble) {
  cycle += 1;
  fsim << "--------------------\n";
  fsim << "Cycle:" << cycle << "\t" << addr << "\t" << assemble << "\n\n";
  fsim << "Registers" << "\n";

  fsim << "R00:\t";
  for (u32 i = 0; i < 8; i++ ) fsim << R[i] << ((i == 7) ? "\n" : "\t");

  fsim << "R08:\t";
  for (u32 i = 8; i < 16; i++) fsim << R[i] << ((i == 15) ? "\n" : "\t");

  fsim << "R16:\t";
  for (u32 i = 16; i < 24; i++) fsim << R[i] << ((i == 23) ? "\n" : "\t");

  fsim << "R24:\t";
  for (u32 i = 24; i < 32; i++) fsim << R[i] << ((i == 31) ? "\n" : "\t");

  fsim << "\nData\n";

  u32 i = 0;
  while (i * 4 + lowest_addr <= largest_addr) {
    fsim << i * 4 + lowest_addr << ":\t";
    for (u32 j = 0; j < 8; j++) {
      fsim << D[i] << ((j == 7) ? "\n" : "\t");
      i++;
    }
  }

  fsim << endl;

}

void Program::run() {
  while (true) {
    auto& instruction = instructions[PC];
    instruction->run();
    trace(instruction->addr, instruction->assemble);
    if (PC == 0XFFFFFFFF) return;
  }
}

void Program::read() {
  string binary;
  u32 raw;
  bool is_break = false;
  
  while (cin >> binary) {
    raw = static_cast<u32>(stoul(binary, 0, 2));
    if (is_break) { 
      instructions.emplace_back(make_unique<SignalInstruction>(raw));
      instructions.back()->translate();
      continue;
    }

    if (get_category(raw) == CONTROL) {
      instructions.emplace_back(make_unique<ControlInstruction>(raw));
      instructions.back()->translate();
      if (GET_OPCODE(raw) == OP_BREAK) is_break = true;
    } else if (get_category(raw) == ARITH) {
      instructions.emplace_back(make_unique<ArithInstruction>(raw));
      instructions.back()->translate();
    }

  }
}